package com.xpalive.utils;

import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.Data;
import org.springframework.beans.BeanUtils;

@Data
public class ListPrune<T, R> {

    private final List<T> oldList;
    private final List<T> newList;
    private final Function<T, R> getIdentity;

    private List<T> keepList;
    private List<T> removeList;
    private List<T> addList;


    public ListPrune(List<T> oldList, List<T> newList, Function<T, R> getIdentity) {
        this.oldList = oldList;
        this.newList = newList;
        this.getIdentity = getIdentity;
        this.prune((oldData, newData) -> BeanUtils.copyProperties(newData, oldData));
    }

    public ListPrune(List<T> oldList, List<T> newList, Function<T, R> getIdentity,
        BiConsumer<T, T> updateConsumer) {
        this.oldList = oldList;
        this.newList = newList;
        this.getIdentity = getIdentity;
        this.prune(updateConsumer);
    }

    protected void prune(BiConsumer<T, T> updateConsumer) {
        this.keepList = oldList.parallelStream()
            .filter(oldItem -> itemExistInList(oldItem, newList, updateConsumer))
            .collect(Collectors.toList());

        oldList.removeIf(oldItem -> itemExistInList(oldItem, this.keepList));
        this.removeList = oldList;
        newList.removeIf(newItem -> itemExistInList(newItem, this.keepList));
        this.addList = newList;
    }

    private boolean itemExistInList(final T item, List<T> dataList) {
        return dataList.parallelStream().anyMatch(
            dataItem -> getIdentity.apply(item).equals(getIdentity.apply(dataItem))
        );
    }

    private boolean itemExistInList(final T item, List<T> dataList,
        BiConsumer<T, T> updateConsumer) {
        return dataList.parallelStream().anyMatch(dataItem -> {
                boolean equals = getIdentity.apply(item).equals(getIdentity.apply(dataItem));
                if (equals) {
                    updateConsumer.accept(item, dataItem);
                }
                return equals;
            }
        );
    }
}
